/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include "type.h"
#include "stack_frame.h"

static struct s_cfront_TraverseTable stack_frame_tab;

static Bool (*in_mem) (declarator);

/* Transform declarators into correspoding SimpleDecl's */
static SimpleDecl
make_simple (Type t)
{
  return Create_SimpleDecl (no_srcinfo, t, NULL);
}

static List_SimpleDecl
simplify (List_declarator l)
{
  List_SimpleDecl s;
  Id_Decl d;
  SimpleDecl sd;
  if (l == NULL) return NULL;
  s = simplify (List_declarator_next(l));
  d = List_declarator_data(l);
  sd = Create_SimpleDecl (declarator_srcinfo(d), declarator_type(d), Id_Decl_id(d));
  SimpleDecl_decl(sd) = d;
  return Create_List_SimpleDecl (sd, s);
}

static declaration type_to_decl (Type t)
{
  String str = "int aap";
  declaration d;
  declarator decl;

  if (Type_tag(t) != StructType_kind && Type_tag(t) != UnionType_kind) {
    str = Front_StrAllocf("%s", Type_name(t, "aap"));
  }
  d = Parse_declaration (str, 0);
  decl = get_Id_Decl(List_declarator_data(declaration_decls(d)));
  declarator_type(decl) = t;
  declarator_my_decl(decl) = d;
  return d;
 }

static List_declarator 
add_decl (declarator d, List_declarator l)
{
  if (!in_mem(d)) return l;
  return Create_List_declarator (d, l);
}

/* d are the declarations of a compound, 
   t the frame of the body of the compound 
*/
static Type 
add_decls (List_declaration d, Type t)
{
  static struct s_cfront_TraverseTable tab;
  List_declarator l;
  List_SimpleDecl s;

  tab.action_Id_Decl[PRE_ACTION] = (void*)add_decl;
  tab.skip_children_FuncDecl = TRUE;
  tab.skip_children_StructDef = TRUE;
  tab.skip_children_UnionDecl = TRUE;
  l = Traverse_List_declaration(d, &tab, NULL);

  l = FRONT_REVERSE(declarator,l);
  s = simplify (l);
  if (t) {
    SimpleDecl sd = Create_SimpleDecl (no_srcinfo, t, NULL);
    declaration d;
    FRONT_APPEND_NODE (s, sd);
    d = type_to_decl (t);
    FRONT_APPEND_NODE (l, d);
  }
  if (d==NULL) return NULL;
  return Create_StructType(no_srcinfo, 0, 0, NULL, d, s);
}

static Type 
make_union (Type t)
{
  declaration d = type_to_decl(t);
  SimpleDecl sd = Create_SimpleDecl (no_srcinfo, t, NULL);
  return Create_UnionType (no_srcinfo, 0, 0, NULL, 
                Create_List_declaration(d,NULL), 
		Create_List_SimpleDecl (sd, NULL));
}

static Type 
add_union (Type t, Type d)
{
  SimpleDecl s;
  if (t==NULL) return d;
  if (d==NULL) return t;
  if (Type_tag(t) != UnionType_kind) t = make_union(t);
  s = make_simple(d);
  FRONT_APPEND_NODE(AggrType_mems(t), s);
  return t;
}

static Type 
stack_frame (compound_statement s, Type t)
{
  List_declaration d = compound_statement_locdecls(s);
  Type t1 = Traverse_List_statement (compound_statement_body(s), &stack_frame_tab, NULL);
  return add_union (t, add_decls (d, t1));
}

Type 
GetStackFrame (functionDefinition f, Bool mem (declarator))
{
  Type frame;

  in_mem = mem;

  stack_frame_tab.skip_children_compound_statement = TRUE;
  stack_frame_tab.action_compound_statement[PRE_ACTION] = (void*)stack_frame;
  
  frame = Traverse_functionDefinition(f, &stack_frame_tab, NULL);
  functionDefinition_stack_frame(f) = frame;
  return frame;
}

static int give_offsets (Type t, int o, int align(int,int));

static int 
give_offset (SimpleDecl t, int o, int align(int,int))
{
  declarator d = SimpleDecl_decl(t);
  int p = o+Type_size(SimpleDecl_type(t));
  p = align (p, Type_align(SimpleDecl_type(t)));
  o = align (o, Type_align(SimpleDecl_type(t)));
  if (d) d = get_Id_Decl(d);
  SimpleDecl_offset(t) = p;
  if (d) Id_Decl_offset(d) = p;
  return give_offsets (SimpleDecl_type(t), o, align);
}

static int 
give_offsets (Type t, int o, int align(int,int))
{ List_SimpleDecl l;
  int p;
  
  if (t==NULL) return o;
  switch (Type_tag(t)) {
    case UnionType_kind:
      p = o;
      for (l = AggrType_mems(t); l; l = List_SimpleDecl_next(l)) {
        SimpleDecl t1 = List_SimpleDecl_data(l);
	int q = give_offset (t1, o, align);
	if (q > p) p = q;
      }
      o = p;
      break;
    case StructType_kind:
      for (l = AggrType_mems(t); l; l = List_SimpleDecl_next(l)) {
        SimpleDecl t1 = List_SimpleDecl_data(l);
	o = give_offset (t1, o, align);
      }
      break;
    default:
      o += Type_size(t);
      break;
  }
  return o;
}

int StackFrameOffsets (Type frame, int align(int,int))
{
  if (!frame) return 0;
  return give_offsets (frame, 0, align);
}
